package com.ldap;

import java.util.Hashtable;
import java.util.Properties;

import javax.naming.AuthenticationException;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Active Directory 操作类
 * 
 * @author jliu
 * 
 */
public class ADOperator {
    private final static Log LOG = LogFactory.getLog(ADOperator.class);

    private DirContext adminCtx;

    private static final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";

    private static final int UF_ACCOUNTDISABLE = 0x0002;// 帐户禁用

    private static final int UF_PASSWD_NOTREQD = 0x0020;// 密码可以不设（为空）

    // private static final int UF_PASSWD_CANT_CHANGE = 0x0040;// 用户不能更改密码

    private static final int UF_NORMAL_ACCOUNT = 0x0200;// 普通帐户

    private static final int UF_DONT_EXPIRE_PASSWD = 0x10000;// 密码永不过期

    private static final int UF_PASSWORD_EXPIRED = 0x800000;// 用户下次登陆时须更改密码

    private static final String PROTOCOL = "ssl";

    private static String ldap_url; // ldap地址

    private static String auth_type;// 认证类型，一般为simple

    private static String manager_dn; // ldap管理员的dn

    private static String manager_password;// ldap管理员密码

    private static String certficationPath;// JDK中导入的证书路径

    private static String certficationPwd;// 证书密码

    private static String domain; // 域名，包括@符号

    private static String baseUserDN;// 用户的DN基础路径

    private static String enabledLCS; // 是否开户用户的LCS功能

    private static String LCServerDN; // LCS服务器的ldap DN

    static {
        Properties p = new Properties();

        try {
            LOG.info("LDAP 开始读取配置文件 ldap.properties......");

            p.load(ADOperator.class.getResourceAsStream("/ldap.properties"));
            ldap_url = p.getProperty("ldap_url");
            auth_type = p.getProperty("auth_type");
            manager_dn = p.getProperty("manager_dn");
            manager_password = p.getProperty("manager_password");
            domain = "@" + p.getProperty("domain");
            enabledLCS = p.getProperty("enabledLCS");
            LCServerDN = p.getProperty("LCServerDN");
            baseUserDN = p.getProperty("baseUserDN");
            certficationPath = p.getProperty("certficationPath");
            certficationPwd = p.getProperty("certficationPwd");

            LOG.info("LDAP 成功读取配置文件 ldap.properties!");

        } catch (Exception e) {
            LOG.error("LDAP 失败读取配置文件 ldap.properties!");

            throw new RuntimeException("LDAP 失败读取配置文件 ldap.properties!", e);
        }
    }

    /**
     * 关闭LDAP连接
     * 
     * @param dirContext
     *            DirContext 已连接的LDAP的Context实例
     */
    private void closeDirContext() {
        try {
            if (adminCtx != null) {
                adminCtx.close();
                adminCtx = null;
            }
        } catch (Exception ex) {
            LOG.error("Not close DirContext !", ex);
        }
    }

    /**
     * 获取 LDAP 服务器连接的方法
     * 
     * @param env
     *            连接LDAP的连接信息
     * @return DirContext - LDAP server的连接
     */
    private void initDirContext() {
        try {
            // 设置系统属性中ssl连接的证书和密码
            System.setProperty("javax.net.ssl.trustStore", certficationPath);
            System.setProperty("javax.net.ssl.trustStorePassword", certficationPwd);

            Hashtable<String, String> env = new Hashtable<String, String>();
            env.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY);
            env.put(Context.PROVIDER_URL, ldap_url);
            env.put(Context.SECURITY_AUTHENTICATION, auth_type);
            env.put(Context.SECURITY_PRINCIPAL, manager_dn);
            env.put(Context.SECURITY_CREDENTIALS, manager_password);
            env.put(Context.SECURITY_PROTOCOL, PROTOCOL);
            // 通过参数连接LDAP/AD
            adminCtx = new InitialDirContext(env);

            if (LOG.isDebugEnabled()) {
                LOG.debug("ldap 连接 AD 成功");
            }

        } catch (NamingException ex) {
            LOG.error("ldap 连接 AD 失败!");
            LOG.error("CONTEXT_FACTORY:" + CONTEXT_FACTORY + ",ldap_url:" + ldap_url
                    + ",auth_type:" + auth_type + ",manager_dn:" + manager_dn
                    + ",manager_password:" + manager_password, ex);

            throw new RuntimeException(ex);
        }
    }

    // ------------------------------------------------------------------------START
    // 所有操作
    // ------------------------------------------------------------------------START
    public ADOperatorResult createUser(ADUserInfo user) {
        ADOperatorResult re = null;
        // ------------------------------------------校验START
        String msg = "";
        if (StringUtils.isBlank(user.getSamAccountName())) {
            msg += "samAccountName(loginId) is required,";
        }
        if (StringUtils.isBlank(user.getOu())) {
            msg += " ou(organization unit) is required,";
        }
        if (StringUtils.isBlank(user.getPassword())) {
            msg += " password is required";
        }
        if (StringUtils.isNotBlank(msg)) {
            re = new ADOperatorResult(false, msg);
            return re;
        }
        // ------------------------------------------校验END

        try {

            if (this.adminCtx == null) {
                this.initDirContext();
            }

            if (StringUtils.isBlank(user.getCn())) {
                user.setCn(user.getSamAccountName());
            }

            String dn = getDN(user.getCn(), user.getOu());// 默认

            if (this.isUserExistByDN(dn)) {

                re = new ADOperatorResult(false, "DN(用户全名,含名称和部门):" + dn + ",已存在!");
                return re;
            }

            Attributes attrs = this.getCommonUserAttrs(user);
            this.addObjclassAttrs(attrs);

            // 控制用户的有效性
            putAttribute(attrs, "userAccountControl",
                    Integer.toString(UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD | UF_PASSWD_NOTREQD));

            // 以下cn 顺便生成了 name, cn属性
            adminCtx.createSubcontext(dn, attrs);
            re = new ADOperatorResult(true, "用户" + user.getSamAccountName() + "创建成功!");

        } catch (NameAlreadyBoundException nameAlreadyBoundEx) {
            LOG.error(nameAlreadyBoundEx.getMessage(), nameAlreadyBoundEx);

            re = new ADOperatorResult(false, "loginId(登陆名):" + user.getSamAccountName() + ",已存在!");

        } catch (Exception e) {
            LOG.error(e.getMessage(), e);

            re = new ADOperatorResult(false, e.getMessage());

        } finally {
            this.closeDirContext();
        }

        LOG.info(re.getMsg());
        return re;
    }

    /**
     * 看是否是管理员
     * 
     * @param principalName
     * @param password
     * @return
     * @throws NamingException
     */
    public ADOperatorResult isAdmin(String principalName, String password) throws NamingException {
        ADOperatorResult re = null;

        if ("Administrator".equals(principalName)) {
            if (manager_password.equals(password)) {
                re = new ADOperatorResult(true, "");
            } else {
                re = new ADOperatorResult(false, "管理员密码错误");
            }

            return re;
        }

        re = this.authentication(principalName, password);
        if (!re.getStatus()) {
            re.setMsg("管理员错误(" + re.getMsg() + ")");
            return re;
        }

        Attributes attrs = this.findUserPrincipal(principalName);
        Attribute isAdminAttr = attrs.get("title");

        if (isAdminAttr.get() != null && "Admin".equals(isAdminAttr.get().toString())) {
            re = new ADOperatorResult(true, "");
        } else {
            re = new ADOperatorResult(false, "操作用户没有管理员权限, 无权进行此项操作");
        }

        return re;
    }

    /**
     * 修改密码需要证书，请确认已经导入了证书JDK中<br>
     * 修改用户时,用户不存在,先创建该用户
     * 
     * @param user
     * @throws Exception
     */
    public ADOperatorResult grantAdmin(ADUserInfo user, boolean grant) {
        ADOperatorResult re = null;

        // ------------------------------------------校验START
        String msg = "";
        if (StringUtils.isBlank(user.getSamAccountName())) {
            msg += "samAccountName(loginId) is required,";
        }

        if (StringUtils.isBlank(user.getAdminAcct())) {
            msg += "Admin acct is required,";
        }
        if (StringUtils.isBlank(user.getAdminPass())) {
            msg += " Admin password is required";
        }

        if (StringUtils.isNotBlank(msg)) {
            re = new ADOperatorResult(false, msg);
            return re;
        }
        // ------------------------------------------校验END

        try {
            // 认证管理员
            re = this.isAdmin(user.getAdminAcct(), user.getAdminPass());
            if (!re.status) {
                return re;
            }

            if (this.adminCtx == null) {
                this.initDirContext();
            }

            Attributes attrs = this.findUserPrincipal(user.getSamAccountName());

            if (attrs == null) {
                re = new ADOperatorResult(false, "loginId(登陆名):" + user.getSamAccountName()
                        + ",不存在,请先创建该用户!");

                return re;
            }

            ModificationItem[] mods = new ModificationItem[1];
            if (grant) {
                mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(
                        "title", "Admin"));
            } else {
                mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(
                        "title", "User"));
            }

            Attribute distinguishedName = attrs.get("distinguishedName");
            adminCtx.modifyAttributes(distinguishedName.get().toString(), mods);

            re = new ADOperatorResult(true, user.getAdminAcct() + "给用户" + user.getSamAccountName()
                    + "授权管理角色成功!");

        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            re = new ADOperatorResult(false, e.getMessage());

        } finally {
            this.closeDirContext();
        }

        LOG.info(re.getMsg());
        return re;
    }

    /**
     * 修改密码需要证书，请确认已经导入了证书JDK中<br>
     * 修改用户时,用户不存在,先创建该用户
     * 
     * @param user
     * @throws Exception
     */
    public ADOperatorResult changePassword(ADUserInfo user) {
        ADOperatorResult re = null;
        // ------------------------------------------校验START
        String msg = "";
        if (StringUtils.isBlank(user.getSamAccountName())) {
            msg += "samAccountName(loginId) is required,";
        }
        if (StringUtils.isBlank(user.getPassword())) {
            msg += " password is required";
        }
        if (StringUtils.isNotBlank(msg)) {
            re = new ADOperatorResult(false, msg);
            return re;
        }
        // ------------------------------------------校验END

        try {

            if (this.adminCtx == null) {
                this.initDirContext();
            }

            Attributes attrs = this.findUserPrincipal(user.getSamAccountName());

            if (attrs == null) {
                re = new ADOperatorResult(false, "loginId(登陆名):" + user.getSamAccountName()
                        + ",不存在,请先创建该用户!");

                return re;
            }

            ModificationItem[] mods = new ModificationItem[1];
            String newQuotedPassword = "\"" + user.getPassword() + "\"";
            byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");

            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(
                    "unicodePwd", newUnicodePassword));

            Attribute distinguishedName = attrs.get("distinguishedName");
            adminCtx.modifyAttributes(distinguishedName.get().toString(), mods);

            re = new ADOperatorResult(true, "用户" + user.getSamAccountName() + "修改密码成功!");

        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            re = new ADOperatorResult(false, e.getMessage());

        } finally {
            this.closeDirContext();
        }

        LOG.info(re.getMsg());
        return re;
    }

    /**
     * 修改用户时,用户不存在,先创建该用户
     * 
     * @param user
     * @throws Exception
     */
    public void modifyUser(ADUserInfo user) throws Exception {
        try {
            if (this.adminCtx == null) {
                this.initDirContext();
            }

            if (!this.isUserExistByDN(user.getSamAccountName())) {
                LOG.info("修改用户时,用户不存在,先创建该用户");
                this.createUser(user);
                return;
            }

            Attributes attrs = this.getCommonUserAttrs(user);
            adminCtx.modifyAttributes(getDN(user.getSamAccountName(), baseUserDN),
                    DirContext.REPLACE_ATTRIBUTE, attrs);

        } catch (Exception e) {
            throw e;
        } finally {
            this.closeDirContext();
        }

    }

    public ADOperatorResult destroyUserByPrincipal(String principalName) {
        ADOperatorResult re = null;
        // ------------------------------------------校验START
        if (StringUtils.isBlank(principalName)) {
            String msg = "samAccountName(loginId) is required,";

            re = new ADOperatorResult(false, msg);
            return re;
        }

        // ------------------------------------------校验END

        try {

            if (this.adminCtx == null) {
                initDirContext();
            }

            Attributes attrs = this.findUserPrincipal(principalName);

            if (attrs == null) {
                re = new ADOperatorResult(false, "删除用户" + principalName + "失败, 用户不存在!");

                return re;
            }

            Attribute distinguishedName = attrs.get("distinguishedName");

            if (this.isUserExistByPrincipal(principalName)) {
                adminCtx.destroySubcontext(distinguishedName.get().toString());

                re = new ADOperatorResult(true, "删除用户" + principalName + "成功!");
            }

        } catch (NamingException e) {
            LOG.error(e.getMessage(), e);

            re = new ADOperatorResult(false, "删除用户" + principalName + "失败!");

        } finally {
            LOG.info(re.getMsg());
            this.closeDirContext();
        }

        return re;
    }

    public void destroyUserByDN(ADUserInfo user) throws NamingException {
        try {
            if (this.adminCtx == null) {
                initDirContext();
            }

            String dn = getDN(user.getCn(), user.getOu());// 默认

            if (this.isUserExistByDN(dn)) {
                adminCtx.destroySubcontext(this.getDN(dn, baseUserDN));
                // 使用userPrinspalName也可以删除
                // ctx.destroySubcontext(userName + domain)
            }

            LOG.info("成功删除用户" + dn);
        } catch (NamingException e) {
            throw e;
        } finally {
            this.closeDirContext();
        }
    }

    public void prohibitUser(ADUserInfo user) throws NamingException {
        try {
            if (this.adminCtx == null) {
                this.initDirContext();
            }

            String dn = getDN(user.getCn(), user.getOu());// 默认
            if (this.isUserExistByDN(dn)) {
                return;
            }

            ModificationItem[] mods = new ModificationItem[1];
            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
                    new BasicAttribute("userAccountControl", Integer.toString(UF_NORMAL_ACCOUNT
                            + UF_PASSWORD_EXPIRED)));
            adminCtx.modifyAttributes(dn, mods);

        } catch (NamingException e) {
            throw e;
        } finally {
            this.closeDirContext();
        }

    }

    // 验证用户是否存在
    public boolean isUserExistByDN(String dn) {
        boolean rtn = true;

        Attributes attrs = findUserDN(dn);
        if (attrs == null) {
            rtn = false;
        }

        return rtn;
    }

    // 验证用户是否存在
    public boolean isUserExistByPrincipal(String principal) {
        boolean rtn = true;

        Attributes attrs = findUserPrincipal(principal);
        if (attrs == null) {
            rtn = false;
        }

        return rtn;
    }

    /**
     * (独立需要打开连接)<br>
     * 
     * 查找用户
     * 
     * @param dn
     * @return
     */
    public Attributes findUserDN(String dn) {
        Attributes reAttrs = null;
        boolean isThisOpen = false;// 标志是否是本方法里打开的上下文

        try {

            if (this.adminCtx == null) {
                this.initDirContext();
                isThisOpen = true;
            }

            reAttrs = adminCtx.getAttributes(dn);

            LOG.info("AD中用户" + dn + "存在!");
        } catch (NameNotFoundException e) {
            LOG.info("AD中用户" + dn + "不存在!");

        } catch (Exception e) {
            LOG.error("ldap查找AD中的用户出错", e);
            throw new RuntimeException(e);

        } finally {

            if (isThisOpen) { // 如果是本方法打开的上下文则关掉上下文
                this.closeDirContext();
            }
        }

        return reAttrs;
    }

    /**
     * (独立需要打开连接)<br>
     * 
     * 查找用户
     * 
     * @param principal
     * @return
     */
    public Attributes findUserPrincipal(String principal) {
        Attributes reAttrs = null;
        boolean isThisOpen = false;// 标志是否是本方法里打开的上下文

        try {
            if (this.adminCtx == null) {
                this.initDirContext();
                isThisOpen = true;
            }

            SearchControls searchCons = new SearchControls(); // 搜索控件
            searchCons.setSearchScope(SearchControls.SUBTREE_SCOPE);// SUBTREE_SCOPE
            searchCons.setCountLimit(20000);// 设置最大返回个数
            searchCons.setTimeLimit(60 * 1000);// 超时一分钟

            // 搜索对象,第一个参数为基目录,第二个参数为fiter，fiter格式可类似于(&(CN=*)(DC=*))
            NamingEnumeration<SearchResult> searchResults = adminCtx.search(baseUserDN, "(CN="
                    + principal + ")", searchCons);

            // 找不到则全局找
            if (!searchResults.hasMore()) {
                searchResults = adminCtx.search(baseUserDN, "(CN=*)", searchCons);
            }

            // 遍历搜索结果
            while (searchResults.hasMore()) {
                // Use debug to see the attributes
                SearchResult result = searchResults.nextElement();

                // 通过属性名称获取搜索结果对象的属性值
                Attributes attrs = result.getAttributes();

                Attribute userPrincipalName = attrs.get("userPrincipalName");
                if (userPrincipalName != null
                        && StringUtils.equals(userPrincipalName.get().toString(), principal
                                + domain)) {

                    reAttrs = attrs;
                    LOG.info("AD中用户" + principal + domain + "存在!");
                }
            }

        } catch (Exception e) {
            LOG.error("ldap查找AD中的用户出错", e);

        } finally {

            if (isThisOpen) { // 如果是本方法打开的上下文则关掉上下文
                this.closeDirContext();
            }
        }

        return reAttrs;
    }

    /**
     * 精确认证<br>
     * 调用简单认证authenticationSample(), 返回失败后再确定是用户名不存在还是密码错误
     * 
     * @param principalName
     * @param password
     * @return
     */
    public ADOperatorResult authentication(String principalName, String password) {
        ADOperatorResult re = null;

        // ------------------------------------------校验START
        String msg = "";
        if (StringUtils.isBlank(principalName)) {
            msg += "samAccountName(loginId) is required,";
        }
        if (StringUtils.isBlank(password)) {
            msg += " password is required";
        }
        if (StringUtils.isNotBlank(msg)) {
            re = new ADOperatorResult(false, msg);
            return re;
        }
        // ------------------------------------------校验END

        re = authenticationSample(principalName, password);

        // ----------------------------------------精确认证START
        if (!re.getStatus()) {

            try {
                // 通过属性名称获取搜索结果对象的属性值
                Attributes attrs = this.findUserPrincipal(principalName);

                if (attrs != null) {
                    re.setMsg("该用户为:" + attrs.get("distinguishedName").get() + ", 输入密码错误!");
                } else {
                    re.setMsg("用户名不存在!");
                }

            } catch (NamingException namingEx) {
                LOG.error(namingEx.getMessage(), namingEx);

            }

        }
        // ----------------------------------------精确认证END
        LOG.info(re.getMsg());
        return re;
    }

    /**
     * 简单认证, 返回成功或失败
     * 
     * @param principalName
     * @param password
     * @return
     */
    public ADOperatorResult authenticationSample(String principalName, String password) {
        DirContext userContext = null;
        ADOperatorResult re = null;

        try {
            System.setProperty("javax.net.ssl.trustStore", certficationPath);
            System.setProperty("javax.net.ssl.trustStorePassword", certficationPwd);

            Hashtable<String, String> env = new Hashtable<String, String>();

            env.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY);
            env.put(Context.PROVIDER_URL, ldap_url);
            env.put(Context.SECURITY_AUTHENTICATION, auth_type);
            env.put(Context.SECURITY_PRINCIPAL, principalName + domain);
            env.put(Context.SECURITY_CREDENTIALS, password);
            env.put(Context.SECURITY_PROTOCOL, PROTOCOL);

            userContext = new InitialDirContext(env);

            if (userContext != null) {
                re = new ADOperatorResult(true, "认证通过!");
            }

        } catch (CommunicationException e) {

            String msg = "没有连接上AD服务器, 认证服务的配置AD的ip或port错误!";
            re = new ADOperatorResult(false, msg);
            LOG.error(msg + e.getMessage(), e);

        } catch (AuthenticationException e) {
            String msg = "用户名或密码错误!";
            re = new ADOperatorResult(false, msg);
            LOG.error(msg + e.getMessage());

        } catch (NamingException e) {
            re = new ADOperatorResult(false, e.getMessage());
            LOG.error(e.getMessage(), e);

        } finally {
            try {
                if (userContext != null) {
                    userContext.close();
                    userContext = null;
                }
            } catch (NamingException ex) {
                LOG.error("Not close userContext !", ex);
            }
        }

        return re;
    }

    // ------------------------------------------------------------------------END
    // 所有操作
    // ------------------------------------------------------------------------END

    // ------------------------------------------------------------------------START
    // 以下都是私有成员, 工具函数
    // ------------------------------------------------------------------------START

    private String getDN(String cn, String ou) {
        StringBuilder sb = new StringBuilder("cn=" + cn + ",");

        if (StringUtils.isNotBlank(ou)) {
            sb.append("ou=" + ou + ",");
        }

        sb.append(baseUserDN);

        return sb.toString();
    }

    /**
     * 以防万一
     */
    protected void finalize() {
        this.closeDirContext();
    }

    // 设置属性
    private void putAttribute(Attributes attrs, String attrName, Object attrValue) {
        if (attrValue != null && attrValue.toString().length() != 0) {
            Attribute attr = new BasicAttribute(attrName, attrValue);
            attrs.put(attr);
        }
    }

    private Attributes addObjclassAttrs(Attributes attrs) {
        Attribute objclass = new BasicAttribute("objectclass");
        objclass.add("top");
        objclass.add("person");
        objclass.add("organizationalPerson");
        objclass.add("user");
        attrs.put(objclass);
        return attrs;
    }

    /**
     * 下面这些是用户中LCS的属性:
     * <p>
     * msRTCSIP-ArchivingEnabled 1 开启存档<br>
     * msRTCSIP-FederationEnabled FALSE <br>
     * msRTCSIP-InternetAccessEnabled FALSE<br>
     * msRTCSIP-OptionFlags 0 此属性在老版本的lcs2005中不存在<br>
     * msRTCSIP-PrimaryHomeServer<br>
     * CN=LC Services,CN=Microsoft,CN=LCS182,CN=Pools,CN=RTC
     * Service,CN=Microsoft,CN=System,DC=bxxk,DC=com,DC=cn<br>
     * msRTCSIP-PrimaryUserAddress sip:lcs1@bxxk.com.cn lcs的帐户名称<br>
     * msRTCSIP-UserEnabled TRUE 是否启用lcs帐户<br>
     */
    private Attributes getCommonUserAttrs(ADUserInfo user) throws Exception {
        Attributes attrs = new BasicAttributes();

        // 设置属性
        putAttribute(attrs, "userPrincipalName", user.getSamAccountName() + domain);
        putAttribute(attrs, "sAMAccountName", user.getSamAccountName());

        if (StringUtils.isNotEmpty(user.getGivenName())) {
            putAttribute(attrs, "mail", user.getEmail());
        }

        if (StringUtils.isNotEmpty(user.getGivenName())) {
            putAttribute(attrs, "givenname", user.getGivenName());
        }
        if (StringUtils.isNotEmpty(user.getSn())) {
            putAttribute(attrs, "sn", user.getSn());
        }
        if (StringUtils.isNotEmpty(user.getDescription())) {
            putAttribute(attrs, "description", user.getDescription());
        }
        if (StringUtils.isNotEmpty(user.getCompany())) {
            putAttribute(attrs, "company", user.getCompany());
        }
        if (StringUtils.isNotEmpty(user.getDepartment())) {
            putAttribute(attrs, "department", user.getDepartment());
        }
        if (StringUtils.isNotEmpty(user.getDisplayName())) {
            putAttribute(attrs, "displayName", user.getDisplayName());
        }
        if (StringUtils.isNotEmpty(user.getPassword())) {
            putAttribute(attrs, "unicodePwd",
                    ("\"" + user.getPassword() + "\"").getBytes("UTF-16LE"));
        }

        if ("true".equalsIgnoreCase(enabledLCS) || "on".equals(enabledLCS)) {
            putAttribute(attrs, "msRTCSIP-ArchivingEnabled", "1");
            putAttribute(attrs, "msRTCSIP-FederationEnabled", "FALSE");
            putAttribute(attrs, "msRTCSIP-InternetAccessEnabled", "FALSE");
            // putAttribute(attrs, "msRTCSIP-OptionFlags", "0");
            putAttribute(attrs, "msRTCSIP-PrimaryHomeServer", LCServerDN);
            putAttribute(attrs, "msRTCSIP-PrimaryUserAddress", "sip:" + user.getSamAccountName()
                    + domain);
            putAttribute(attrs, "msRTCSIP-UserEnabled", "TRUE");
        }

        return attrs;
    }
    // ------------------------------------------------------------------------END
    // 以下都是私有成员, 工具函数
    // ------------------------------------------------------------------------END
}
